import { Sound } from "@babylonjs/core/Audio/sound";
import { DialogueImage } from '../texture/icon-image/dialogue-image';
import { TransformNode } from "@babylonjs/core/Meshes/transformNode";
import { Vector2, Vector3 } from "@babylonjs/core/Maths/math.vector";
import { Scene } from "@babylonjs/core/scene";
import { MeshBuilder } from "@babylonjs/core/Meshes/meshBuilder";
import { Texture } from "@babylonjs/core/Materials/Textures/texture";
import { StandardMaterial } from "@babylonjs/core/Materials/standardMaterial";
import { Color3 } from "@babylonjs/core/Maths/math.color";
import { AbstractMesh } from "@babylonjs/core/Meshes/abstractMesh";
import { CanvasTextureText } from "../texture/canvas-texture-text";
import { AnimationFrame } from '../animation/animation-frame';
import { BabylonContainer } from "../base/babylon-container";


/**
 * 台词对象
 * @param key 键名
 * @param duration 时长
 * @param volumn 音量
 * @param sound 声音
 * @param caption 字幕内容
 * @param captionColor 字幕颜色
 * @param captionBackgroundColor 字幕背景颜色
 */
interface IDialogueLine {
    key: string,
    duration: number,
    volumn?: number,
    sound?: Sound,
    caption?: string,
    captionColor?: string,
    captionBackgroundColor?: string,
}

/**
 * 对白参数对象
 * @param bubbleSize 对话泡泡大小
 * @param bubbleColor 对话泡泡颜色
 * @param rowLength 单行最多字数
 * @param title 字幕前置标题
 * @param lineBillboard 字幕朝向模式
 * @param lineSize 字幕大小
 * @param vivid 字幕清晰度（默认2）
 * @param anchor 字幕锚点
 * @param captionColor 字幕颜色
 * @param captionBackgroundColor 字幕背景颜色
 * @param captionBorderRadius 字幕圆角半径
 * @param punctuation 字幕标题与内容之间的符号
 * @param lineRenderingGroupId 字幕的渲染组
 * @param pixelRatioMode 使用像素比修正（0：用像素比的倒数修正，1：不修正，2：用像素比修正）
 */
interface IDialogueOptions {
    bubbleSize?: number,
    bubbleColor?: string,
    rowLength?: number,
    title?: string,
    lineBillboard?: number,
    lineSize?: number,
    vivid?: number,
    anchor?: Vector2,
    captionColor?: string,
    captionBackgroundColor?: string,
    captionBorderRadius?: number,
    punctuation?: string,
    lineRenderingGroupId?: number,
    pixelRatioMode?: 0 | 1 | 2
}


/**
 * 对白
 * @param name 名称
 * @param scene 所在场景
 * @param options 参数对象
 */
class Dialogue {

    private _options: IDialogueOptions;
    private _lines: IDialogueLine[] = [];
    private _target?: TransformNode;
    private _bubble?: TransformNode;
    private _lineMesh!: AbstractMesh;
    private _bubbleMesh!: AbstractMesh;
    private _req = {};
    private _curLine?: IDialogueLine;

    /**
     * 字幕容器对象
     */
    public readonly container: TransformNode;

    public readonly name: string;
    public readonly scene: Scene;

    constructor(name: string, scene: Scene, options?: IDialogueOptions) {
        const ops = options || {};
        ops.bubbleSize = ops.bubbleSize || 1;
        ops.bubbleColor = ops.bubbleColor || '#ffffff';
        ops.rowLength = ops.rowLength || 20;
        ops.lineBillboard = ops.lineBillboard || TransformNode.BILLBOARDMODE_NONE;
        ops.lineSize = ops.lineSize || 1;
        ops.vivid = ops.vivid || 2;
        ops.anchor = ops.anchor || new Vector2(0.5, 1);
        ops.captionColor = ops.captionColor || '#ffffff';
        ops.captionBackgroundColor = ops.captionBackgroundColor || 'rgba(0,0,0,0.4)';
        ops.captionBorderRadius = ops.captionBorderRadius || 5;
        ops.punctuation = ops.punctuation || ':';
        ops.lineRenderingGroupId = ops.lineRenderingGroupId || 0;
        ops.pixelRatioMode = ops.pixelRatioMode === undefined ? 0 : ops.pixelRatioMode;
        this._options = ops;

        this.name = name;
        this.scene = scene;
        this.container = new TransformNode(this.name + '-container', this.scene);
        this.container.billboardMode = ops.lineBillboard;
        this.initializeLineMesh();
    }

    /**
     * 添加台词
     * @param paras 参数对象
     */
    public addLine(paras: IDialogueLine): void {
        for (const l of this._lines) {
            if (l.key === paras.key) return;
        }
        const ln: IDialogueLine = {
            key: paras.key,
            duration: paras.duration,
            sound: paras.sound,
            caption: paras.caption,
            volumn: paras.volumn || 1,
            captionColor: paras.captionColor,
            captionBackgroundColor: paras.captionBackgroundColor,
        };
        this._lines.push(ln);
    }

    /**
     * 激活台词
     * @param key 键名
     */
    public activeLine(key?: string | number): void {
        AnimationFrame.cancel(this._req);
        this._lineMesh.isVisible = false;
        if (this._bubbleMesh) this._bubbleMesh.isVisible = false;
        if (this._curLine && this._curLine.sound) this._curLine.sound.stop();
        this._curLine = undefined;
        if ((key === undefined) || (key === null)) return;

        if (typeof key === 'string') {
            for (const l of this._lines) {
                if (l.key === key) this._curLine = l;
            }
        } else {
            this._curLine = this._lines[key];
        }

        if (!this._curLine) return;
        let content = '';
        if (this._options.title) {
            content += this._options.title + this._options.punctuation + '';
        }
        if (this._curLine.caption) {
            content += this._curLine.caption + '';
        } else {
            content += '......';
        }
        const material = this._lineMesh.material as StandardMaterial;
        if (material.opacityTexture) material.opacityTexture.dispose();
        const pr = BabylonContainer.engine?.getHardwareScalingLevel()!;
        const tex = new CanvasTextureText(this.name + '-line-material-texture', {
            text: content,
            fontColor: this._curLine.captionColor || this._options.captionColor,
            rowLength: this._options.rowLength,
            vivid: this._options.vivid,
            border: this._curLine.captionBackgroundColor || this._options.captionBackgroundColor,
            background: this._curLine.captionBackgroundColor || this._options.captionBackgroundColor,
            borderRadius: this._options.captionBorderRadius,
            marginVertical: 10,
            pixelRatio: this._options.pixelRatioMode === 0 ? 1 / pr : (this._options.pixelRatioMode === 1 ? 1 : pr),
        }, this.scene);
        material.opacityTexture = tex.texture;
        material.emissiveTexture = tex.texture;

        if (this._curLine.sound) {
            this._curLine.sound.setVolume(this._curLine.volumn!);
            this._curLine.sound.play();
        }
        if (this._bubbleMesh) this._bubbleMesh.isVisible = true;
        this._lineMesh.scaling.set(tex.width, tex.height, 1);
        this._lineMesh.isVisible = true;

        const itv = this._curLine.duration * 1000;
        AnimationFrame.throttleByTime(this._req, itv, () => {
            this._lineMesh.isVisible = false;
            if (this._bubbleMesh) this._bubbleMesh.isVisible = false;
            if (this._curLine!.sound) this._curLine?.sound.stop();
        }, false);
    }

    /**
     * 设置对话泡泡的父级对象
     * @param target 目标对象
     * @param offset 位置偏移
     */
    public attach(target: TransformNode, offset: Vector3): void {
        this._target = target;
        this._bubble = this.createBubble();
        this._bubble.parent = this._target;
        this._bubble.position.copyFrom(offset);
    }

    /**
     * 解除对话泡泡的父级对象
     */
    public detach(): void {
        if (!this._bubble) return;
        this._bubble.parent = null;
        this._bubble.dispose(false, true);
        this._target = undefined;
    }

    /**
     * 初始化台词网格
     */
    private initializeLineMesh(): void {
        const pn = MeshBuilder.CreatePlane(this.name + '-line-mesh', { size: this._options.lineSize! }, this.scene);
        const trsX = (0.5 - this._options.anchor!.x) * this._options.lineSize!;
        const trsY = (this._options.anchor!.y - 0.5) * this._options.lineSize!;
        pn.position.set(trsX, trsY, 0);
        pn.bakeCurrentTransformIntoVertices();
        const mat = new StandardMaterial(this.name + '-line-material', this.scene);
        const tex = new CanvasTextureText(this.name + '-line-material-texture', {
            text: ' ',
        }, this.scene);
        mat.opacityTexture = tex.texture;
        mat.disableLighting = true;
        pn.material = mat;
        pn.isPickable = false;
        pn.isVisible = false;
        pn.parent = this.container;
        pn.position.set(0, 0, 0);
        pn.rotation.set(0, 0, 0);
        pn.renderingGroupId = this._options.lineRenderingGroupId!;
        this._lineMesh = pn;
    }

    /**
     * 创建对话泡泡
     * @returns 对话泡泡的容器对象
     */
    private createBubble(): TransformNode {
        const bc = new TransformNode(this.name + '-bubble-container', this.scene);
        const bb = MeshBuilder.CreatePlane(this.name + '-bubble-mesh', { size: 1 }, this.scene);
        bb.isPickable = false;
        const tex = new Texture(DialogueImage(2, '#ffff00'), this.scene);
        const mat = new StandardMaterial(this.name + '-bubble-material', this.scene);
        mat.disableLighting = true;
        mat.opacityTexture = tex;
        mat.emissiveColor = Color3.FromHexString(this._options.bubbleColor!);
        bb.material = mat;
        bb.parent = bc;
        bb.position.set(0, 0, 0);
        bb.billboardMode = TransformNode.BILLBOARDMODE_ALL;
        bb.isVisible = false;
        this._bubbleMesh = bb;
        return bc;
    }

}


export { Dialogue };
export type { IDialogueLine, IDialogueOptions };